<!doctype HTML public "-//W3C//DTD HTML 4.0 Frameset//EN">
<html>
<title>Bloomberg Development Environment</title>
<html>
<pre>
// Copyright 2014-2023 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// bmqt_uri.h                                                         -*-C++-*-
#ifndef INCLUDED_BMQT_URI
#define INCLUDED_BMQT_URI

//@PURPOSE: Provide value-semantic type and utilities for a BlazingMQ queue
//URI.
//
//@CLASSES:
//  bmqt::Uri        : value-semantic type representing a URI
//  bmqt::UriParser  : utility to parse a string into a URI
//  bmqt::UriBuilder : builder mechanism to create a URI
//
//@DESCRIPTION: This component provides a value-semantic type, &#39;bmqt::Uri&#39;
// representing a URI for a BlazingMQ queue.  A &#39;bmqt:Uri&#39; can be built by
// parsing from a string representation, using the &#39;bmqt::UriParser&#39;, or built
// using the &#39;bmqt::UriBuilder&#39;.
//
//@SEE_ALSO:
//----------
// https://tools.ietf.org/html/rfc3986
//
///URI format
///----------
// In a nutshell, a URI representing an application queue managed by a
// BlazingMQ broker on a given machine looks like one of the following:
//..
//  bmq://ts.trades.myapp/my.queue
//  bmq://ts.trades.myapp.~bt/my.queue
//  bmq://ts.trades.myapp/my.queue?id=foo
//..
// where:
//
//: o The URI scheme is always &quot;bmq&quot;.
//
//: o The URI authority is the name of BlazingMQ domain (such as
//    &quot;ts.trades.myapp&quot;)
//:   as registered with the BlazingMQ infrastructure.  The domain name may
//:   contain alphanumeric characters, dots and dashes (it has to match the
//:   following regular expression: &#39;[-a-zA-Z0-9\\._]+&#39;).  The domain may be
//:   followed by an optional tier, introduced by the &quot;.~&quot; sequence and
//:   consisting of alphanumeric characters and dashes.  The &quot;.~&quot; sequence is
//:   not part of the tier.
//
//: o The URI path is the name of the queue (&quot;my.queue&quot; above) and may contain
//:   alphanumeric characters, dashes, underscores and tild (it has to match
//:   the following regular expression: &#39;[-a-zA-Z0-9_~\\.]+&#39;).  The queue name
//:   must be less than &#39;bmqt::Uri::k_QUEUENAME_MAX_LENGTH&#39; characters long.
//
//: o The name of the queue (&quot;my.queue&quot; above) may contain alphanumeric
//:   characters and dots.
//
//: o The URI may contain an optional query with a key-value pair.  Currently
//:   supported keys are:
//:   o !id!: the corresponding value represents a name that will be used by
//:     BlazingMQ broker to uniquely identify the client.
//
//: o The URI fragment part is currently unused.
//
///Usage Example 1
///---------------
// First, call the &#39;initialize&#39; method of the &#39;UriParser&#39;.  This call is only
// needed one time; you can call it when your task starts.
//
// Note that the &#39;bmq&#39; library takes care of that, so users of &#39;bmq&#39; don&#39;t
// have to explicitly do it themselves.
//..
//  bmqt::UriParser::initialize();
//..
// Then, parse a URI string created on the stack to populate a &#39;Uri&#39; object.
// The parse function takes an optional error string which is populated with a
// short error message if the URI is not formatted correctly.
//..
//   bsl::string input = &quot;bmq://my.domain/queue&quot;;
//   bmqt::Uri   uri(allocator);
//   bsl::string errorDescription;
//
//   int         rc = bmqt::UriParser::parse(&amp;uri, &amp;errorDescription, input);
//   if (rc != 0) {
//      BALL_LOG_ERROR &lt;&lt; &quot;Invalid URI [error: &quot; &lt;&lt; errorDescription &lt;&lt; &quot;]&quot;;
//   }
//   assert(rc == 0);
//   assert(error == &quot;&quot;);
//   assert(uri.scheme()    == &quot;bmq&quot;);
//   assert(uri.domain()    == &quot;my.domain&quot;);
//   assert(uri.queue()     == &quot;queue&quot;);
//..
//
/// Usage Example 2
///----------------
// Instantiate a &#39;Uri&#39; object with a string representation of the URI and an
// allocator.
//..
//  bmqt::Uri uri(&quot;bmq://my.domain/queue&quot;, allocator);
//  assert(uri.scheme() == &quot;bmq&quot;);
//  assert(uri.domain() == &quot;my.domain&quot;);
//  assert(uri.queue()  == &quot;queue&quot;);
//..

///Thread Safety
///-------------
//: o &#39;bmqt::UriBuilder&#39; is NOT thread safe
//: o &#39;bmqt::UriParser&#39; should be thread safe: the component depends on
//:   &#39;bdepcre_regex&#39; that is a wrapper around &quot;pcre.h&quot;.  See
//:    http://www.pcre.org/pcre.txt.


// BMQ
#include &lt;bmqscm_version.h&gt;

// BDE
#include &lt;ball_log.h&gt;
#include &lt;bsl_cstddef.h&gt;
#include &lt;bsl_iosfwd.h&gt;
#include &lt;bsl_string.h&gt;
#include &lt;bslh_hash.h&gt;
#include &lt;bslma_usesbslmaallocator.h&gt;
#include &lt;bslmf_nestedtraitdeclaration.h&gt;


namespace BloombergLP {

// FORWARD DECLARATION
namespace bmqt { class UriBuilder; }
namespace bmqt { struct UriParser; }

namespace bmqt {


                                 // =========
                                 // class Uri
                                 // =========

class Uri {
    // Value semantic type representing a URI

  public:
    // PUBLIC CONSTANTS
    static const int k_QUEUENAME_MAX_LENGTH = 64;
        // The maximum authorized length for the queue name part of the URI.

  private:
    // CLASS-SCOPE CATEGORY
    BALL_LOG_SET_CLASS_CATEGORY(&quot;BMQT.URI&quot;);

  private:
    // FRIENDS
    friend struct UriParser;
    friend class UriBuilder;
    template &lt;class HASH_ALGORITHM&gt;
    friend void hashAppend(HASH_ALGORITHM&amp; hashAlgo, const Uri&amp; uri);

  private:
    // DATA
    bsl::string       d_uri;            // The full URI

    bslstl::StringRef d_scheme;         // URI scheme (must be &quot;bmq&quot;).

    bslstl::StringRef d_authority;      // URI authority (domain + optional
                                        // tier)

    bslstl::StringRef d_domain;         // URI domain

    bslstl::StringRef d_tier;           // URI tier

    bslstl::StringRef d_path;           // URI path (i.e queue name).

    bslstl::StringRef d_query_id;       // Optional application id, part of the
                                        // URI query if present.

    bool              d_wasParserInitialized;
                                        // Flag indicating whether the URI
                                        // parser was initialized (and whether
                                        // shutdown should be called on it at
                                        // destruction)

  private:
    // PRIVATE MANIPULATORS
    void reset();
        // Reset this object to the default value.

    void copyImpl(const Uri&amp; src);
        // Implementation of the copy of the specified &#39;src&#39; URI into this
        // object.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Uri, bslma::UsesBslmaAllocator)

    // CREATORS
    explicit Uri(bslma::Allocator *allocator = 0);
        // Constructor of an invalid Uri with all fields empty, using the
        // optionally specified &#39;allocator&#39;.

    Uri(const Uri&amp;        original,
        bslma::Allocator *allocator = 0); // IMPLICIT
        // Copy constructor, create a new Uri having the same values as the
        // specified &#39;original&#39;, and using the optionally specified
        // &#39;allocator&#39;.

    Uri(const bsl::string&amp;  uri,
        bslma::Allocator   *allocator = 0);                         // IMPLICIT
    Uri(const bslstl::StringRef&amp;  uri,
        bslma::Allocator         *allocator = 0);                   // IMPLICIT
    Uri(const char       *uri,
        bslma::Allocator *allocator = 0);                           // IMPLICIT
        // Implicit constructor of this object from the specified &#39;uri&#39; string
        // using the optionally specified &#39;allocator&#39;.  If the &#39;uri&#39; input
        // string doesn&#39;t not represent a valid URI, this object is left in an
        // invalid state (isValid() will return false).

    ~Uri();
        // Destructor.

    // MANIPULATORS
    Uri&amp; operator=(const Uri&amp; rhs);
        // Set the value of this object to the specified &#39;rhs&#39;.

    // ACCESSORS
    const bsl::string&amp; asString() const;
        // Return the string representation of this URI.

    bool isValid() const;
        // Return true if this object represents a valid URI.

    bool isCanonical() const;
        // Return true if this object represents a canonical URI.

    const bslstl::StringRef&amp; scheme() const;
    const bslstl::StringRef&amp; authority() const;
    const bslstl::StringRef&amp; path() const;
        // Return the corresponding (raw) part of the URI, matching to the URI
        // RFC terminology.

    const bslstl::StringRef&amp; qualifiedDomain() const;
    const bslstl::StringRef&amp; domain() const;
    const bslstl::StringRef&amp; tier() const;
    const bslstl::StringRef&amp; queue() const;
    const bslstl::StringRef&amp; id() const;
        // Return the corresponding (extracted) part of the URI, provided as
        // convenient accessors using the BlazingMQ terminology.

    bslstl::StringRef canonical() const;
        // Return the canonical form of the URI. Note that canonical form
        // includes everything except the query part of the URI.

    bsl::ostream&amp; print(bsl::ostream&amp; stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output &#39;stream&#39; at the (absolute
        // value of) the optionally specified indentation &#39;level&#39; and return a
        // reference to &#39;stream&#39;.  If &#39;level&#39; is specified, optionally specify
        // &#39;spacesPerLevel&#39;, the number of spaces per indentation level for
        // this and all of its nested objects.  If &#39;level&#39; is negative,
        // suppress indentation of the first line.  If &#39;spacesPerLevel&#39; is
        // negative format the entire output on one line, suppressing all but
        // the initial indentation (as governed by &#39;level&#39;).  If &#39;stream&#39; is
        // not valid on entry, this operation has no effect.
};

// FREE OPERATORS
bool operator==(const Uri&amp; lhs,
                const Uri&amp; rhs);
    // Return &#39;true&#39; if the specified &#39;rhs&#39; object contains the value of the
    // same type as contained in the specified &#39;lhs&#39; object and the value
    // itself is the same in both objects, return false otherwise.

bool operator!=(const Uri&amp; lhs,
                const Uri&amp; rhs);
    // Return &#39;false&#39; if the specified &#39;rhs&#39; object contains the value of the
    // same type as contained in the specified &#39;lhs&#39; object and the value
    // itself is the same in both objects, return &#39;true&#39; otherwise.

bool operator&lt;(const Uri&amp; lhs,
               const Uri&amp; rhs);
    // Return &#39;true&#39; if the specified &#39;lhs&#39; object compares less than the
    // specified &#39;rhs&#39; object.


bsl::ostream&amp; operator&lt;&lt;(bsl::ostream&amp; stream,
                         const Uri&amp;    rhs);
    // Format the specified &#39;rhs&#39; to the specified output &#39;stream&#39; and return
    // a reference to the modifiable &#39;stream&#39;.


                              // ================
                              // struct UriParser
                              // ================

struct UriParser {
    // Utility namespace of methods for parsing URI strings into &#39;Uri&#39; objects.

    // CLASS METHODS
    static void initialize(bslma::Allocator *allocator = 0);
        // Initialize the &#39;UriParser&#39;.  Note that this will compile the regular
        // expression used by &#39;parseUri&#39;.  This method only needs to be called
        // once before any other method, but can be called multiple times
        // provided that for each call to &#39;initialize&#39; there is a corresponding
        // call to &#39;shutdown&#39;.  Use the optionally specified &#39;allocator&#39; for
        // any memory allocation, or the &#39;global&#39; allocator if none is
        // provided.  Note that specifying the allocator is provided for test
        // drivers only, and therefore users should let it default to the
        // global allocator.

    static void shutdown();
        // Pendant operation of the &#39;initialize&#39; one.  Note that behaviour
        // after calling the &#39;.parse()&#39; method of the &#39;UriParser&#39; after
        // &#39;shutdown&#39; has been called is undefined.  The number of calls to
        // &#39;shutdown&#39; must equal the number of calls to &#39;initialize&#39;, without
        // corresponding &#39;shutdown&#39; calls, to fully destroy the parser.  It is
        // safe to call &#39;initialize&#39; after calling &#39;shutdown&#39;.  Behaviour is
        // undefined if &#39;shutdown&#39; is called without &#39;initialize&#39; first being
        // called.

    static int parse(Uri                      *result,
                     bsl::string              *errorDescription,
                     const bslstl::StringRef&amp;  uriString);
        // Parse the specified &#39;uriString&#39; into the specified &#39;result&#39; object
        // if &#39;uriString&#39; is a valid URI, otherwise load the specified
        // &#39;errorDescription&#39; with a description of the syntax error present in
        // &#39;uriString&#39;.  Return 0 on success and non-zero if &#39;uriString&#39; does
        // not have a valid syntax.  Note that &#39;errorDescription&#39; may be null
        // if the caller does not care about getting error messages.  The
        // behavior is undefined unless &#39;initialize&#39; has been called
        // previously.

  private:
    // CLASS-SCOPE CATEGORY
    BALL_LOG_SET_CLASS_CATEGORY(&quot;BMQT.URI&quot;);
};


                              // ================
                              // class UriBuilder
                              // ================

class UriBuilder {
    // This component implements a mechanism, &#39;bmqt::UriBuilder&#39;, that can be
    // used for creating queue URI for BMQ.

  private:
    // DATA
    Uri d_uri; // Placeholder object for URI object being built.

  private:
    // NOT IMPLEMENTED
    UriBuilder(const UriBuilder&amp;);
    UriBuilder&amp; operator=(const UriBuilder&amp;);
        // Copy constructor and assignment operator not implemented.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(UriBuilder, bslma::UsesBslmaAllocator)

    // CREATORS
    explicit UriBuilder(bslma::Allocator *allocator = 0);
        // Create a new UriBuilder using the optionally specified &#39;allocator&#39;.

    explicit UriBuilder(const bmqt::Uri&amp;  uri,
                        bslma::Allocator *allocator = 0);
        // Create a new UriBuilder initialized with the specified &#39;uri&#39; and
        // using the optionally specified &#39;allocator&#39;.

    // MANIPULATORS
    UriBuilder&amp; setDomain(const bslstl::StringRef&amp; value);
    UriBuilder&amp; setTier(const bslstl::StringRef&amp; value);
    UriBuilder&amp; setQualifiedDomain(const bslstl::StringRef&amp; value);
    UriBuilder&amp; setQueue(const bslstl::StringRef&amp; value);
    UriBuilder&amp; setId(const bslstl::StringRef&amp; value);
        // Set the corresponding field of the URI to the specified &#39;value&#39;.
        // The behavior is undefined unless &#39;value&#39; remains valid until URI has
        // been built by invoking &#39;uri()&#39;.  &#39;setDomain()&#39; and &#39;setTier()&#39;
        // should be preferred over &#39;setQualifiedDomain()&#39; whenever possible.

    void reset();
        // Reset all fields of this builder.

    // ACCESSORS
    int uri(Uri         *result,
            bsl::string *errorDescription = 0) const;
        // Build and populate the specified &#39;result&#39; with the built URI,
        // returning 0 on success, or return non-zero on error, populating the
        // optionally specified &#39;errorDescription&#39; if provided.
};


// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================


                                 // ---------
                                 // class Uri
                                 // ---------

inline
Uri&amp;
Uri::operator=(const Uri&amp; rhs)
{
    copyImpl(rhs);

    return *this;
}

inline
const bsl::string&amp;
Uri::asString() const
{
    return d_uri;
}

inline
bool
Uri::isValid() const
{
    // URI can only be set using either &#39;UriBuilder&#39; or &#39;UriParser&#39;, which are
    // resetting the d_uri on failure.
    return !d_uri.empty();
}

inline
bool
Uri::isCanonical() const
{
    return d_query_id.isEmpty();
}

inline
const bslstl::StringRef&amp;
Uri::scheme() const
{
    return d_scheme;
}

inline
const bslstl::StringRef&amp;
Uri::authority() const
{
    return d_authority;
}

inline
const bslstl::StringRef&amp;
Uri::path() const
{
    return d_path;
}

inline
const bslstl::StringRef&amp;
Uri::domain() const
{
    return d_domain;
}

inline
const bslstl::StringRef&amp;
Uri::qualifiedDomain() const
{
    return d_authority;
}

inline
const bslstl::StringRef&amp;
Uri::tier() const
{
    return d_tier;
}

inline
const bslstl::StringRef&amp;
Uri::queue() const
{
    return d_path;
}

inline
const bslstl::StringRef&amp;
Uri::id() const
{
    return d_query_id;
}

inline
bslstl::StringRef
Uri::canonical() const
{
    size_t queryBeginPos = d_uri.find_first_of(&#39;?&#39;);
    if (bsl::string::npos == queryBeginPos) {
        return d_uri;                                                 // RETURN
    }

    return bslstl::StringRef(d_uri.c_str(), queryBeginPos);
}

// FREE FUNCTIONS
template &lt;class HASH_ALGORITHM&gt;
void
hashAppend(HASH_ALGORITHM&amp; hashAlgo,
           const Uri&amp;      uri)
{
    using bslh::hashAppend;  // for ADL
    hashAppend(hashAlgo, uri.d_uri);  // hashes full uri string
}


                              // ----------------
                              // class UriBuilder
                              // ----------------

inline
UriBuilder&amp;
UriBuilder::setDomain(const bslstl::StringRef&amp; value)
{
    d_uri.d_domain = value;
    return *this;
}

inline
UriBuilder&amp;
UriBuilder::setTier(const bslstl::StringRef&amp; value)
{
    d_uri.d_tier = value;
    return *this;
}

inline
UriBuilder&amp;
UriBuilder::setQueue(const bslstl::StringRef&amp; value)
{
    d_uri.d_path = value;
    return *this;
}

inline
UriBuilder&amp;
UriBuilder::setId(const bslstl::StringRef&amp; value)
{
    d_uri.d_query_id = value;
    return *this;
}


}  // close package namespace

// FREE OPERATORS

inline
bool
bmqt::operator==(const bmqt::Uri&amp; lhs,
                 const bmqt::Uri&amp; rhs)
{
    return (lhs.asString() == rhs.asString());
}

inline
bool
bmqt::operator!=(const bmqt::Uri&amp; lhs,
                 const bmqt::Uri&amp; rhs)
{
    return (lhs.asString() != rhs.asString());
}

inline
bool
bmqt::operator&lt;(const bmqt::Uri&amp; lhs,
                const bmqt::Uri&amp; rhs)
{
    return (lhs.asString() &lt; rhs.asString());
}

inline
bsl::ostream&amp;
bmqt::operator&lt;&lt;(bsl::ostream&amp;    stream,
                 const bmqt::Uri&amp; rhs)
{
    return rhs.print(stream, 0, -1);
}

}  // close enterprise namespace

#endif
</pre>
</body>
</html>
